package com.javaspeak.concurrency.pattern.actor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import org.apache.log4j.Logger;

/**
 * @author John Dickerson
 */
public class ActorInvocationHandler implements InvocationHandler {

	public Logger logger = Logger.getLogger( ActorInvocationHandler.class );

	final private BlockingQueue<MethodRequest> methodRequestBlockingQueue;
	final private Object originalObject;


	public ActorInvocationHandler(
		BlockingQueue<MethodRequest> methodRequestBlockingQueue,
			Object originalObject ){

		this.methodRequestBlockingQueue = methodRequestBlockingQueue;
		this.originalObject = originalObject;
	}


	public Object invoke(Object object, Method method, Object[] args)
			throws Throwable {

		// logger.debug( "method name = " + method.getName() +
		//		" object class = " + object.getClass().getName() );

		if ( method.isAnnotationPresent( Asynchronous.class ) ){

			methodRequestBlockingQueue.put(
				new MethodRequest( originalObject, method, args, null ) );

			// logger.debug( "Finished Asynchronous method, " + method.getName() );

			return null;
		}
		else if ( method.isAnnotationPresent( Blocking.class ) ){

			final BlockingQueue<MethodResponse> replyQueue =
				new ArrayBlockingQueue<MethodResponse>( 1 );

			methodRequestBlockingQueue.put(
					new MethodRequest( originalObject, method, args, replyQueue ) );

			Object response = replyQueue.take().getResponse();

			// logger.debug( "Got response for synchronous method, " +
			//		method.getName() );

			return response;
		}
		else {

			// logger.warn( "Invoked without passing to method queue:" +
			//		method.getName() + " " + Thread.currentThread() );

			Object result = method.invoke( originalObject, args );
			return result;
		}
	}
}
